~ 運籌帷幄之中,決勝於千里之外 ~
相關程式碼:https://github.com/slindevel/modern-aws-marathon
虛擬化讓我們能在文件腳本中定義資源,即基礎設施及代碼(Infrastructure as Code,IaC)
CloudFormation 能幫助您模型化與設定 AWS 資源的服務。我們建立一個描述所有所需之 AWS 資源的範本 (如 Amazon EC2 執行個體或 Amazon RDS 資料庫執行個體),而 CloudFormation 負責為您佈建與設定這些資源。
這個服務中有兩大重要名詞:
我們先來看一段官方的 workshop 裡的定義:
An AWS CloudFormation template is a declaration of the AWS resources that make up a stack.
template 他是一種資源的**「宣告」,雖然說是用代碼寫資源配置,感覺上是比較偏向「靜態」定義資源**。
接著我們來解析一下 template 的文件,yaml & json 格式最大的差別在於yaml可以加上註釋,所以之後的說明我們都以 yaml 格式為主,比較好解說,另外 yaml 的文件看的時候要注意 「縮排」 & 「階層」,json ↔ yaml 是可以互轉的喔!
我們先從 yaml 文件的 Top-Level objects 開始:
AWSTemplateFormatVersion: 'version date' (optional) # version of the CloudFormation template. Only accepted value is '2010-09-09'
Description: 'String' (optional) # a text description of the Cloudformation template
Metadata: 'template metadata' (optional) # objects that provide additional information about the template
Parameters: 'set of parameters' (optional) # a set of inputs used to customize the template
Rules: 'set of rules' (optional) # a set of rules to validate the parameters provided at deployment/update
Mappings: 'set of mappings' (optional) # a mapping of keys and associated values
Conditions: 'set of conditions' (optional) # conditions that control whether certain resources are created
Transform: 'set of transforms' (optional) # for serverless applications
Resources: 'set of resources' (required) # a components of your infrastructure
Hooks: 'set of hooks' (optional) # Used for ECS Blue/Green Deployments
Outputs: 'set of outputs' (optional) # values that are returned whenever you view your stack's properties
讓我們一個個來看看:
AWSTemplateFormatVersion: '2010-09-09'
Description: A simple queue for the demo purpose
# Day4/cfn-s3-demo.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Create S3 Bucket named 'slin-cfn-s3-demo'
Resources:
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: slin-cfn-s3-demo
      VersioningConfiguration:
        Status: Enabled
# cloudformation stack name: cf-s3-demo
$ aws cloudformation create-stack \
		--stack-name cf-s3-demo \
		--template-body file://Day4/cf-s3-demo.yaml \
		--capabilities CAPABILITY_IAM
$ aws cloudformation delete-stack --stack-name cf-s3-demo.   #記得清除資源
# Day4/cf-s3-parameters-demo.yaml
# aws cloudformation create-stack \
#	  --stack-name cf-s3-demo \
#		--template-body file://Day4/cf-s3-parameters-demo.yaml \
#		--capabilities CAPABILITY_IAM
AWSTemplateFormatVersion: 2010-09-09
Description: Create S3 Bucket named 'slin-cf-s3-demo'
Parameters:
  BucketName:
    # Default: slin-cf-s3-demo
    Type: String
    Description: 'A bucket name to set.'
Resources:
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: !Ref BucketName
      VersioningConfiguration:
        Status: Enabled
# output: 
# An error occurred (ValidationError) when calling the CreateStack operation: 
# Parameters: [BucketName] must have values
$ aws cloudformation create-stack \
	  --stack-name cf-s3-demo \ 
		--template-body file://Day4/cf-s3-parameters-demo.yaml \
		--capabilities CAPABILITY_IAM \
		--parameters ParameterKey=BucketName,ParameterValue=slin-cf-s3-demo
$ aws s3 ls    #檢查s3 bucket slin-cf-s3-demo 是否建立
$ aws cloudformation delete-stack --stack-name cf-s3-demo.   #記得清除資源
# Day4/cf-s3-outputs-demo.yaml
...
Outputs:   #add to the tail
  MyStacksRegion:
    Value: !Ref 'AWS::Region'
$ aws cloudformation create-stack \
	  --stack-name cf-s3-demo \ 
		--template-body file://Day4/cf-s3-parameters-demo.yaml \
		--capabilities CAPABILITY_IAM \
		--parameters ParameterKey=BucketName,ParameterValue=slin-cf-s3-demo
$ aws cloudformation describe-stacks --stack-name cf-s3-demo --query "Stacks[].Outputs"
[
    [
        {
            "OutputKey": "BucketARN",
            "OutputValue": "arn:aws:s3:::slin-cf-s3-demo",
            "Description": "The ARN of the bucket."
        },
        {
            "OutputKey": "MyStacksRegion",
            "OutputValue": "ap-northeast-1"
        }
    ]
]
$ aws cloudformation delete-stack --stack-name cf-s3-demo   #記得清除資源
Mappings:
	Mapping01:
		TopLevelKey01:
			SecondLevelKey01: Value01
			SecondLevelKey02: Value02
		TopLevelKey02:
			SecondLevelKey01: Value03
			SecondLevelKey02: Value04
...
# Day4/cf-s3-mappings-demo.yaml
Parameters:
...
	Environment:
	    Type: String
	    AllowedValues:
	      - Dev
	      - Test
	      - Prod
	    Description: 'Select an environment.'
Mappings:
  EnvironmentToBucketSuffix:
    Dev:
      Suffix: 'this-is-dev'
    Test:
      Suffix: 'use-only-for-testing'
    Prod:
      Suffix: 'be-careful-it-is-production'
...
$ aws cloudformation create-stack \
          --stack-name cf-s3-demo \
          --template-body file://Day4/cf-s3-mappings-demo.yaml \
          --capabilities CAPABILITY_IAM \
          --parameters ParameterKey=BucketName,ParameterValue=slin-cf-s3-demo \
                       ParameterKey=Environment,ParameterValue=foobar
An error occurred (ValidationError) when calling the CreateStack operation: Parameter 'Environment' must be one of AllowedValues
$ aws cloudformation create-stack \
          --stack-name cf-s3-demo \
                --template-body file://Day4/cf-s3-mappings-demo.yaml \
                --capabilities CAPABILITY_IAM \
                --parameters ParameterKey=BucketName,ParameterValue=slin-cf-s3-demo \
                             ParameterKey=Environment,ParameterValue=Dev
$ aws s3 ls
...
2023-08-13 00:07:33 slin-cf-s3-demo-this-is-dev
...
# Day4/cf-s3-conditions-demo.yaml
...
Conditions:
  isProduction: !Equals [ !Ref Environment, Prod]
Resources:
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: !If [ isProduction, slin-cf-s3-prod, slin-cf-s3-demo ]
      VersioningConfiguration:
        Status: Enabled
...
$ aws cloudformation create-stack \
          --stack-name cf-s3-demo \
          --template-body file://Day4/cf-s3-conditions-demo.yaml \
          --capabilities CAPABILITY_IAM \
          --parameters ParameterKey=BucketName,ParameterValue=slin-cf-s3-demo \
                       ParameterKey=Environment,ParameterValue=Prod
$ aws s3 ls
...
2023-08-13 00:29:16 slin-cf-s3-prod
...
$ aws cloudformation delete-stack --stack-name cf-s3-demo    #delete stack again
$ aws cloudformation create-stack \
          --stack-name cf-s3-demo \
          --template-body file://Day4/cf-s3-conditions-demo.yaml \
          --capabilities CAPABILITY_IAM \
          --parameters ParameterKey=BucketName,ParameterValue=slin-cf-s3-demo \
                       ParameterKey=Environment,ParameterValue=Dev
$ aws s3 ls
...
2023-08-13 00:38:26 slin-cf-s3-demo
...

AWS CloudFormation 提供多個內建函數,可協助您管理您的堆疊。在範本中使用內部函數,將值指派給要到執行時間才能使用的屬性。 ~ by AWS ~
我們直接從一個簡單的範例來看看這些內部函數如何使用,這個範例會建立一個 EC2 Instance,使用到了兩個內置函數:
最後會在 EC2 看到 name:t2.micro-webserver 的 Instance
# Day4/cf-intrinsic-functions.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: AWS CloudFormation Intrinsic functions Sample
Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t2.small
    Description: 'Enter t2.micro or t2.small. Default is t2.micro.'
  AmiID:
    Type: AWS::EC2::Image::Id
    Description: 'The ID of the AMI.'
  # Add AmiId parameter here
Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      # Use !Ref function in ImageId property
      ImageId: !Ref AmiID
      InstanceType: !Ref InstanceType
      Tags:
        - Key: Name
          Value: !Join [ '-', [ !Ref InstanceType, webserver ] ]
執行看看,其中 AmiID 需要用指令查詢,這個 AmiID是會變動的唷
ParameterValue 要帶入查詢到的 AmiID
AmiID 指的是 Amazon Machine Image,AWS 虛擬機器映像檔
$ aws cloudformation create-stack \
--stack-name cf-day4-intrinsic-functions 
--template-body file://Day4/cf-intrinsic-functions.yaml 
--parameters ParameterKey="AmiID",ParameterValue=<AmiId>
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:<AccountId>:stack/cf-day4-intrinsic-functions/d5fa02b0-4e61-11ee-91ec-067eb25769dd"
}
# query statck status
$ aws cloudformation describe-stacks --stack-name cf-day4-intrinsic-functions
# clean up resource
$ aws cloudformation delete-stack --stack-name cf-day4-intrinsic-functions
如何查詢 AMI ID ?
$ aws ec2 describe-images --owners self amazon
{
    "Images": [
        {
            "Architecture": "x86_64",
            "CreationDate": "2022-11-14T23:19:27.000Z",
            "ImageId": "ami-07b14acc0c4d11fcc",
            "ImageLocation": "amazon/amzn2-ami-minimal-hvm-2.0.20221103.3-x86_64-ebs",
            "ImageType": "machine",
            "Public": true,
            "OwnerId": "137112412989",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "SnapshotId": "snap-016aca1439d9dc407",
:
到這裡為止,是否對 cloudformation 上手了呢,AWS 官方有提供一個 github 裡面有各式各樣的可以參考的 template ,網址是:https://github.com/awslabs/aws-cloudformation-templates.git
筆者認為現在比較流行的 IaC 是使用 terraform 或是 AWS CDK,CDK 不用說 DevOps 專門
CloudFormation 與 Terreform 各有優缺點,兩者通常會交互使用,尤其是 Terreform 可以跨雲部署道路是更寬廣了
參考資料:
https://blog.awsfundamentals.com/infrastructure-as-code-on-aws-an-introduction
https://blog.awsfundamentals.com/infrastructure-as-code-on-aws-an-introduction
https://docs.aws.amazon.com/zh_tw/AWSCloudFormation/latest/UserGuide/Welcome.html
https://catalog.workshops.aws/cfn101/en-US/basics/templates/template-anatomy
https://keshavbist.com.np/anatomy-of-aws-cloudformation-templates
https://rickhw.github.io/2017/03/31/AWS/Study-Notes-CloudFormation-Template-Anatomy/
https://godleon.github.io/blog/AWS/AWS-SOA-CloudFormation/